package com.stars.easyms.rest.client.template;

import com.stars.easyms.base.constant.HttpHeaderConstants;
import com.stars.easyms.base.trace.EasyMsTraceSynchronizationManager;
import com.stars.easyms.base.util.DateTimeUtil;
import com.stars.easyms.base.util.SpringBootUtil;
import com.stars.easyms.base.util.TraceUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.client.*;

import java.io.IOException;
import java.net.URI;
import java.util.List;

/**
 * <p>className: EasyMsRestTemplate</p>
 * <p>description: 增加EasyMs自定义逻辑的RestTemplate类</p>
 *
 * @author guoguifang
 * @version 1.7.3
 * @date 2021/3/3 8:10 下午
 */
@Slf4j
public class EasyMsRestTemplate extends RestTemplate {

    public EasyMsRestTemplate() {
        super();
    }

    public EasyMsRestTemplate(ClientHttpRequestFactory requestFactory) {
        super(requestFactory);
    }

    public EasyMsRestTemplate(List<HttpMessageConverter<?>> messageConverters) {
        super(messageConverters);
    }

    @Override
    @Nullable
    protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
                              @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {

        Assert.notNull(url, "URI is required");
        Assert.notNull(method, "HttpMethod is required");

        // 获取全局追踪ID，用于微服务日志全链路跟踪，如果为空则创建一个
        String traceId = TraceUtil.getTraceId(EasyMsTraceSynchronizationManager.getTraceId());
        String applicationName = SpringBootUtil.getApplicationName();
        String requestId = TraceUtil.getTraceId();
        String requestTime = DateTimeUtil.now();
        String asyncId = EasyMsTraceSynchronizationManager.getAsyncId();
        String requestPath = url.getPath();

        ClientHttpResponse response = null;
        try {
            ClientHttpRequest request = createRequest(url, method);
            if (requestCallback != null) {
                requestCallback.doWithRequest(request);
            }

            // 将参数放入请求头中
            HttpHeaders requestHeaders = request.getHeaders();
            requestHeaders.set(HttpHeaderConstants.TRACE_KEY, traceId);
            requestHeaders.set(HttpHeaderConstants.HEADER_KEY_REQUEST_PATH, requestPath);
            requestHeaders.set(HttpHeaderConstants.HEADER_KEY_REQUEST_SYS, applicationName);
            requestHeaders.set(HttpHeaderConstants.HEADER_KEY_REQUEST_ID, requestId);
            requestHeaders.set(HttpHeaderConstants.HEADER_KEY_REQUEST_TIME, requestTime);
            if (asyncId != null) {
                requestHeaders.set(HttpHeaderConstants.HEADER_KEY_ASYNC_ID, asyncId);
            }

            // 将用户信息放入请求头中
            String userInfo = EasyMsTraceSynchronizationManager.getCurrentUserInfo();
            if (userInfo != null) {
                requestHeaders.set(HttpHeaderConstants.HEADER_KEY_USER_INFO, userInfo);
            }

            log.info("[调用服务-请求]-[请求地址: {}]-[请求系统: {}]-[请求ID: {}]{}-[请求时间: {}]{}.",
                    requestPath, applicationName, requestId, TraceUtil.getAsyncIdTrace(asyncId),
                    requestTime, request.getMethod() != null ? "-[请求Method: " + request.getMethod().name() + "]" : "");

            // 执行http请求
            response = request.execute();
            handleResponse(url, method, response);
            return responseExtractor != null ? responseExtractor.extractData(response) : null;
        } catch (IOException ex) {
            String resource = url.toString();
            String query = url.getRawQuery();
            resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
            throw new ResourceAccessException("I/O error on " + method.name() +
                    " request for \"" + resource + "\": " + ex.getMessage(), ex);
        } finally {
            if (response != null) {
                HttpHeaders responseHeaders = response.getHeaders();
                String responseSys = responseHeaders.getFirst(HttpHeaderConstants.HEADER_KEY_RESPONSE_SYS);
                String responseTime = responseHeaders.getFirst(HttpHeaderConstants.HEADER_KEY_RESPONSE_TIME);
                log.info("[调用服务-响应]-[请求地址: {}]-[请求系统: {}]-[服务系统: {}]-[请求ID: {}]{}-[响应时间: {}]-[接收响应时间: {}].",
                        requestPath, applicationName, TraceUtil.withUnknown(responseSys),
                        requestId, TraceUtil.getAsyncIdTrace(asyncId),
                        TraceUtil.withUnknown(responseTime), DateTimeUtil.now());

                response.close();
            }
        }
    }

}
